package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.heima.article.dto.ArticleStreamMessage;
import com.heima.article.entity.ApArticle;
import com.heima.article.service.IApArticleService;
import com.heima.article.service.IHotArticleService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
public class HotArticleServiceImpl implements IHotArticleService {

    @Autowired
    private IApArticleService articleService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public void compute() {
        // 每天凌晨1点运行
        // 查询前5天的文章  从当天的0点0分0秒往前推5天
        Date now = new Date();
        Date to = new Date(now.getYear(), now.getMonth(), now.getDate());
        // 往前推5天
        Date from = new Date(to.getTime() - 5 * 24 * 60 * 60 * 1000);
        LambdaQueryWrapper<ApArticle> query = new LambdaQueryWrapper<>();
        query.ge(ApArticle::getPublishTime, from);
        query.lt(ApArticle::getPublishTime, to);
        // 过滤已删除已下架的文章
        query.eq(ApArticle::getIsDown, false);
        query.eq(ApArticle::getIsDelete, false);

        List<ApArticle> list = articleService.list(query);
        // 计算文章分值
        for (ApArticle article : list) {
            double score = computeScore(article);
            // 为首页和每个频道缓存

            // 为推荐首页缓存数据到redis
            String key = "hot_article_first_page_0";
            // 存文章的JSON数据,主要存页面展示的数据 要保存的是不变的数据
            ApArticle toCache = new ApArticle();
            toCache.setId(article.getId());
            toCache.setTitle(article.getTitle());
            toCache.setAuthorId(article.getAuthorId());
            toCache.setAuthorName(article.getAuthorName());
            toCache.setChannelId(article.getChannelId());
            toCache.setChannelName(article.getChannelName());
            toCache.setPublishTime(article.getPublishTime());
            toCache.setCreatedTime(article.getCreatedTime());
            toCache.setStaticUrl(article.getStaticUrl());
            toCache.setImages(article.getImages());
            toCache.setLayout(article.getLayout());
            String value = JSON.toJSONString(toCache);
            // 使用zset数据结构来保存
            redisTemplate.opsForZSet().add(key, value, score);
            // 设置过期时间
            redisTemplate.expire(key, 24 * 60 - 1, TimeUnit.MINUTES);

            // 为每个频道首页缓存数据到redis
            String channelKey = "hot_article_first_page_" + article.getChannelId();
            redisTemplate.opsForZSet().add(channelKey, value, score);
            redisTemplate.expire(channelKey, 24 * 60 - 1, TimeUnit.MINUTES);
        }
    }

    @Override
    public void updateArticle(ArticleStreamMessage message) {
        // 根据文章id查询文章
        ApArticle article = articleService.getById(message.getArticleId());
        // 重新计算文章的分值
        // 计算本次聚合消息的增量分值
        double scorePlus = computeScore(message);
        // 判断这篇文章是否已经在redis中缓存了
        String key = "hot_article_first_page_0";
        // 存文章的JSON数据,主要存页面展示的数据 要保存的是不变的数据
        ApArticle toCache = new ApArticle();
        toCache.setId(article.getId());
        toCache.setTitle(article.getTitle());
        toCache.setAuthorId(article.getAuthorId());
        toCache.setAuthorName(article.getAuthorName());
        toCache.setChannelId(article.getChannelId());
        toCache.setChannelName(article.getChannelName());
        toCache.setPublishTime(article.getPublishTime());
        toCache.setCreatedTime(article.getCreatedTime());
        toCache.setStaticUrl(article.getStaticUrl());
        toCache.setImages(article.getImages());
        toCache.setLayout(article.getLayout());
        String value = JSON.toJSONString(toCache);
        Double score = redisTemplate.opsForZSet().score(key, value);
        // 如果已经缓存过,需要把当前增量的分值加到redis的分数上
        if (score != null) {
            redisTemplate.opsForZSet().incrementScore(key, value, scorePlus);
            redisTemplate.opsForZSet().incrementScore("hot_article_first_page_" + article.getChannelId(), value, scorePlus);
        } else {
            // 如果没有缓存过,需要先计算文章的历史分值,再加上增量的分值,然后添加到redis
            double historyScore = computeScore(article);
            redisTemplate.opsForZSet().add(key, value, historyScore + scorePlus);
            // 设置过期时间  从当前时间---第二天0点 todo
            redisTemplate.opsForZSet().add("hot_article_first_page_" + article.getChannelId(), value, historyScore + scorePlus);
        }
        // 更新文章表数据
        LambdaUpdateWrapper<ApArticle> update = new LambdaUpdateWrapper<>();
        // update ap_article set views = views + ? ,likes = likes + ?,comment = comment + ?,collection = collection + ? where id = ?
        update.eq(ApArticle::getId, article.getId());
        update.setSql(" views = views + " + message.getView());
        update.setSql(" likes = likes + " + message.getLike());
        update.setSql(" comment = comment + " + message.getComment());
        update.setSql(" collection = collection + " + message.getCollect());

        // update.set(ApArticle::getLikes, article.getLikes() + message.getLike()); // 在高并发的时候可能会出现数据不一致问题
        articleService.update(update);
    }

    /**
     * 计算本次聚合消息的增量分值
     *
     * @param message
     * @return
     */
    private double computeScore(ArticleStreamMessage message) {
        double score = 0;
        score += message.getView() * 1 * 3;
        score += message.getLike() * 3 * 3;
        score += message.getComment() * 5 * 3;
        score += message.getCollect() * 8 * 3;
        return score;
    }

    /**
     * 计算文章分值
     *
     * @param article
     * @return
     */
    private double computeScore(ApArticle article) {
        // 阅读权重 1
        // 点赞权重 3
        // 评论权重 5
        // 收藏权重 8
        double score = 0;
        if (article.getViews() != null) {
            score += 1 * article.getViews();
        }
        if (article.getLikes() != null) {
            score += 3 * article.getLikes();
        }
        if (article.getComment() != null) {
            score += 5 * article.getComment();
        }
        if (article.getCollection() != null) {
            score += 8 * article.getCollection();
        }
        return score;
    }
}
